package server

import (
	"context"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/xuelang-group/sh-solver-scheduler/internal/controller"
	"github.com/xuelang-group/sh-solver-scheduler/internal/controller/private"
	"github.com/xuelang-group/sh-solver-scheduler/internal/controller/task"
	"github.com/xuelang-group/sh-solver-scheduler/internal/server/middleware"
	"github.com/xuelang-group/sh-solver-scheduler/pkg/server"
	"github.com/xuelang-group/sh-solver-scheduler/pkg/utils"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

func New() (*Server, error) {
	taskController := task.NewTaskController()
	controllers := []controller.Controller{
		taskController,
	}

	// 平台内部接口
	internalTaskController := private.NewInternalTaskController()
	internalControllers := []controller.Controller{
		internalTaskController,
	}

	e := gin.New()
	// TODO: use need middleware
	e.Use(
		gin.Recovery(),
		middleware.CORSMiddleware(),
	)

	return &Server{
		engine:              e,
		controllers:         controllers,
		internalControllers: internalControllers,
	}, nil
}

type Server struct {
	engine *gin.Engine
	// 对外服务
	controllers []controller.Controller
	// 对内服务
	internalControllers []controller.Controller
}

// graceful shutdown
func (s *Server) Run() error {
	defer s.Close()

	s.initRouter()

	// TODO:
	addr := fmt.Sprintf("%s:%d", "0.0.0.0", 9000)
	logrus.Infof("Start server on: %s", addr)

	server := &http.Server{
		Addr:    addr,
		Handler: s.engine,
	}

	go func() {
		if err := server.ListenAndServe(); err != http.ErrServerClosed {
			logrus.Fatalf("Failed to start server, %v", err)
		}
	}()

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(3)*time.Second)
	defer cancel()

	ch := <-sig
	logrus.Infof("Receive signal: %s", ch)

	return server.Shutdown(ctx)
}

func (s *Server) Close() {
}

func (s *Server) initRouter() {
	root := s.engine

	// register non-resource routers
	root.GET("/", server.WrapFunc(s.getRoutes))
	if gin.Mode() != gin.ReleaseMode {
	}

	api := root.Group("/api/v1")
	controllers := make([]string, 0, len(s.controllers))
	for _, router := range s.controllers {
		router.RegisterRoute(api)
		controllers = append(controllers, router.Name())
	}

	internalApi := root.Group("/internal")
	for _, router := range s.internalControllers {
		router.RegisterRoute(internalApi)
		controllers = append(controllers, router.Name())
	}

	logrus.Infof("server enabled controllers: %v", controllers)
}

func (s *Server) getRoutes() []string {
	paths := utils.NewString()
	for _, r := range s.engine.Routes() {
		if r.Path != "" {
			paths.Insert(r.Path)
		}
	}
	return paths.Slice()
}
